﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ConsolidationTool.Common;
using Microsoft.SharePoint;
using ConsolidationTool.WebParts.Layouts.AjaxServices;
using System.Web;
using System.IO;

namespace ConsolidationTool.WebParts
{
    public class MappingLocator
    {
        private SearchEntity ReportContext;
        public List<CacheSPItem> Caches { get; set; }
        public Dictionary<string, string> FieldCache { get; set; }


        public MappingLocator(SearchEntity ReportContext)
        {
            this.ReportContext = ReportContext;
            this.Caches = new List<CacheSPItem>();
            this.FieldCache = new Dictionary<string, string>();
        }

        #region Cache

        private string GetFieldCacheIdentity(ARTReportFromItem from, SearchEntity se)
        {
            return from.Encryption() + se.Encryption();
        }

        private string GetFieldCache(ARTReportFromItem from, SearchEntity se)
        {
            string cacheIdentity = GetFieldCacheIdentity(from, se);

            if (FieldCache.Count == 0 || !FieldCache.ContainsKey(cacheIdentity)) return string.Empty;

            return FieldCache[cacheIdentity];
        }
        private void SetFieldCache(ARTReportFromItem from, SearchEntity se, string value)
        {
            string cacheIdentity = GetFieldCacheIdentity(from, se);
            FieldCache.Add(cacheIdentity, value);
        }

        #endregion

        public CacheSPItem GetItem(ARTReportFromItem from, SearchEntity se)
        {
            DateTime now = DateTime.Now;

            int month = se.Month;
            if (from.budget)
                month = 0;

            string identity = Utilities.CreateItemIndentity(from.list, se.Entity, se.Year + "", month == 0 ? "" : month + "");
            CacheSPItem item = Caches.FirstOrDefault(p => p.Identity == identity);
            if (item != null)
                return item;


            var spitem = BusinessService.GetListItem(se.Entity, se.Year, month, from.list);
            if (spitem == null)
            {
                //Utilities.Log(string.Format("[Item Empty] item :{0} ", identity));
                return new CacheSPItem(se.Year, se.Month, from.list);
            }
            item = new CacheSPItem(spitem);
            Caches.Add(item);
            return item;
        }

        internal RQDataItem GetMappingRowData(ARTReportRow row)
        {
            DateTime now = DateTime.Now;
            //CurrentRow = row;
            var rdi = new RQDataItem();
            rdi.title = row.title;
            rdi.emptyRow = row.emptyRow;
            rdi.skip = row.skip;
            // row.digits = row.digits.IsNullOrEmpty() ? "0" : row.digits;

            int testIndex = 1;

            if (row.emptyRow)
                return rdi;

            try
            {
                #region--currentGroup
                if (row.currentGroup)
                {
                    int count = row.Datasource[row.index[0]].values.Count;
                    row.digits = row.digits.IsNullOrEmpty() ? "0" : row.digits;

                    for (int i = 0; i < count; i++)
                    {
                        string express = PreprocessExpress(row.equ);
                        for (int a = 0; a < row.index.Length; a++)
                        {
                            string _value = row.Datasource[row.index[a]].values[i];
                            express = BusinessService.equReplace(express, "{" + a + "}", _value);
                        }

                        express = BusinessService.GetDivision(express);
                        string error = string.Empty, result = Evaluator.Eval(express, out error);
                        decimal digitTempValue = 0.00M;
                        if (decimal.TryParse(result, out digitTempValue))
                        {
                            result = digitTempValue.ToString("N" + row.digits);
                        }
                        rdi.values.Add(result);
                    }
                    return rdi;
                }
                #endregion

                #region --group
                if (row.group != null && row.group.ToLower() == "report")
                {
                    row.digits = row.digits.IsNullOrEmpty() ? "0" : row.digits;
                    rdi.skip = row.skip;
                    if (row.emptyRow)
                    {
                        rdi.values = new List<string>(row.fieldCount);
                        rdi.emptyRow = true;
                    }
                    if (row.titleRow)
                        rdi.values = BusinessService.GetReportTitle(ReportContext, row.fieldCount);
                    if (row.M12)
                        rdi.values = Common.BusinessService.GetLast12MonthFieldValues(ReportContext.Entity, ReportContext.Year, ReportContext.Month, row.list, row.equFields, row.equ, row.withCurrentMonth, row.avg, row.R12);
                    if (row.Budget)
                        rdi.values = Common.BusinessService.GetBudgetLast12MonthFieldValues(ReportContext.Entity, ReportContext.Year, ReportContext.Month, row.list, row.equFields, row.equ, row.withCurrentMonth, row.compress, row.onlycm, row.otherExp);

                    // Utilities.Log(string.Format("[Brochure Report] report row title :{0} need:{1}", rdi.title, DateTime.Now - now));
                    int i = 0;
                    rdi.values.ForEach(p =>
                    {

                        decimal digitTempValue = 0.00M;
                        if (decimal.TryParse(p, out digitTempValue))
                        {
                            rdi.values[i] = digitTempValue.ToString("N" + row.digits);
                        }
                        i++;
                    });
                    return rdi;
                }
                #endregion

                row.fields.ForEach(field =>
                {
                    //CurrentField = field;
                    field.list = field.list.IsNullOrEmpty() ? row.list : field.list;
                    field.equ = field.equ.IsNullOrEmpty() ? row.equ : field.equ;
                    field.digits = field.digits.IsNullOrEmpty() ? row.digits : field.digits;

                    string express = PreprocessExpress(field.equ), result = string.Empty; int index = 0; bool empty = false;

                    if (field.emptycolumn || !field.value.IsNullOrEmpty())//如果是空列，要么是指定值
                    {
                        empty = true;
                        result = field.value;
                    }
                    else if (field.selfrow)//计算列
                    {
                        //dengshuai 2014-10-30
                        if (field.type == "forecast")
                        {
                            string[] str = express.Split('+');
                            if (row.month == 10)
                            {
                                express = str[0] + "+" + str[1] + "+" + str[2];
                            }
                            else if (row.month == 11)
                            {
                                express = str[0] + "+" + str[1];
                            }
                            else if (row.month == 12)
                            {
                                express = str[1] + "+" + str[2] + "+" + str[3];
                            }
                        }

                        if (express.IsNullOrEmpty())
                            result = rdi.values[field.index[0]];
                        else
                        {
                            for (int i = 0; i < field.index.Length; i++)
                            {
                                string tmp = string.Empty;

                                //if (field.type.StartsWith("MergeForecast"))
                                //{

                                //    tmp = GetMergeValueFromEntity(row, field.index[i], field.type.Split('_').Last());
                                //}
                                //else
                                //{
                                tmp = rdi.values[field.index[i]];
                                //}
                                express = BusinessService.equReplace(express, "{" + i + "}", tmp);
                            }
                        }
                    }
                    else if (field.selftable)
                    {
                        express = PreprocessExpress(field.equ);
                        for (int a = 0; a < field.index.Length; a++)
                        {
                            string exp = field.express[a];
                            string _value = "0.00";
                            if (exp == "CM")
                                _value = row.Datasource[field.index[a]].values[field.id - 1];
                            else if (exp == "YTD")
                                _value = row.Datasource[field.index[a]].values.GetRange(0, field.id).Sum(p => BusinessService.decimalParse(p)).ToString("N" + field.digits);

                            express = BusinessService.equReplace(express, "{" + a + "}", _value);
                        }
                    }
                    else
                    {
                        field.from.ForEach(from =>
                        {
                            from.list = from.list.IsNullOrEmpty() ? field.list : from.list;
                            from.identity = from.identity.IsNullOrEmpty() ? row.identity : from.identity;
                            from.method = from.method.IsNullOrEmpty() ? field.method : from.method;

                            var method = this.GetType().GetMethod(from.method);

                            result = method.Invoke(this, new object[] { from }) + "";

                            if (!express.IsNullOrEmpty())
                                express = BusinessService.equReplace(express, "{" + index + "}", result);

                            index++;
                        });
                    }


                    //Utilities.Log(string.Format("[{0}] Title: {1} {2}", testIndex, row.title, express));

                    string _res = DynamicEval(row, field, empty, express, result);

                    rdi.values.Add(_res);

                    testIndex++;
                });

                //Utilities.Log(string.Format("[Brochure Report] table row title :{0} need:{1}", rdi.title, DateTime.Now - now));

            }
            catch (Exception ex)
            {
                Utilities.Log(string.Format("[Exception]-{2} title :{0} identity:{1} message:{3}", row.title, row.identity, ReportContext.ReportTemplateName, ex.Message));
                //throw ex;
            }
            return rdi;
        }

        private string GetMergeValueFromEntity(ARTReportRow row, int p, string mergeType)
        {
            var field = row.fields[p];
            string express = PreprocessExpress(field.equ), result = string.Empty; int index = 0; bool empty = false;

            field.from.ForEach(from =>
            {
                from.list = from.list.IsNullOrEmpty() ? field.list : from.list;
                from.identity = from.identity.IsNullOrEmpty() ? row.identity : from.identity;
                from.method = from.method.IsNullOrEmpty() ? field.method : from.method;
                from.merge = mergeType;

                var method = this.GetType().GetMethod(from.method);

                result = method.Invoke(this, new object[] { from }) + "";

                if (!express.IsNullOrEmpty())
                    express = BusinessService.equReplace(express, "{" + index + "}", result);

                index++;
            });

            string _res = DynamicEval(row, field, empty, express, result);
            return _res;
        }

        private string GetMergeValueFromEntityItem(ARTReportFromItem from, SearchEntity se)
        {

            var item = GetItem(from, se);

            var value = item[GetFieldIdentity(from)];
            SetFieldCache(from, se, value);

            return value;
        }

        private string DynamicEval(ARTReportRow row, ARTReportItem field, bool empty, string express, string result)
        {
            string error = string.Empty;
            //TODO 处理多个字段问题
            if (!express.IsNullOrEmpty())
            {
                express = BusinessService.GetDecimalParse(express);
                result = Evaluator.Eval(express, out error);
            }
            if (express.IndexOf('/') != -1)
                field.digits = field.digits.IsNullOrEmpty() ? "3" : field.digits;

            if (field.digits.IsNullOrEmpty())
                field.digits = "0";

            decimal digitTempValue = 0.00M;
            if (decimal.TryParse(result, out digitTempValue))
            {
                result = digitTempValue.ToString("N" + field.digits);
            }

            if (!empty && !error.IsNullOrEmpty())
            {
                result = "0";
                Utilities.Log(string.Format("[Eval Falied] - {2} Title:{1} Express:{0}", express, row.title, ReportContext.ReportTemplateName));
            }

            return result;
        }

        private string GetFieldIdentity(ARTReportFromItem from)
        {
            if (from.komode || from.budget)
                return from.identity + "|" + from.field;
            return from.field + from.identity;
        }

        private void Preprocess(ARTReportFromItem from, SearchEntity se)
        {
            from.budget = from.list.IndexOf("Budget_") != -1 || from.list.IndexOf("_Budget") != -1;
            from.komode = from.list.IndexOf("Actual_") != -1 || from.list.IndexOf("Forecast_") != -1 || from.list.StartsWith("R_") || from.list.StartsWith("Common_");

            if (!from.field.IsNullOrEmpty()) return;

            if (from.budget)
                from.field = from.field.IsNullOrEmpty() ? BusinessService.GetEngMonth(se.Month) : from.field;
            else if (!from.komode)
                from.field = from.field.IsNullOrEmpty() ? "CM_" : from.field;
            else if (from.komode)//IMPORTANT CHANGE
                from.field = from.method.Split('_').Last();

            from.field = from.profix + from.field + from.suffix;

        }

        private string PreprocessExpress(string exp)
        {
            if (exp.IsNullOrEmpty()) return string.Empty;

            return exp.Replace("_M_", ReportContext.Month + "").Replace("_CM_", ReportContext.Month + "");
        }

        #region Method

        //取当月数据
        public string CM(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            return _CM(from, se);
        }

        public string M_1(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            if (from.month != 0)
            {
                se.UpdateDate(from.month);
                from.month = 0;//clear month information
            }

            se.SelectedDateTime = se.SelectedDateTime.AddMonths(-1);
            se.Year = se.SelectedDateTime.Year;
            se.Month = se.SelectedDateTime.Month;

            return _CM(from, se);
        }

        public string N_1_MA1(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.SelectedDateTime = se.SelectedDateTime.AddYears(-1);
            se.SelectedDateTime = se.SelectedDateTime.AddMonths(1);
            se.Year = se.SelectedDateTime.Year;
            se.Month = se.SelectedDateTime.Month;
            return _CM(from, se);
        }

        public string N_1_MA2(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.SelectedDateTime = se.SelectedDateTime.AddYears(-1);
            se.SelectedDateTime = se.SelectedDateTime.AddMonths(2);
            se.Year = se.SelectedDateTime.Year;
            se.Month = se.SelectedDateTime.Month;
            return _CM(from, se);
        }
        public string N_1_MA3(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.SelectedDateTime = se.SelectedDateTime.AddYears(-1);
            se.SelectedDateTime = se.SelectedDateTime.AddMonths(3);
            se.Year = se.SelectedDateTime.Year;
            se.Month = se.SelectedDateTime.Month;
            return _CM(from, se);
        }

        public string MA1(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.SelectedDateTime = se.SelectedDateTime.AddMonths(1);
            se.Year = se.SelectedDateTime.Year;
            se.Month = se.SelectedDateTime.Month;
            return _CM(from, se);
        }
        public string MA2(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.SelectedDateTime = se.SelectedDateTime.AddMonths(2);
            se.Year = se.SelectedDateTime.Year;
            se.Month = se.SelectedDateTime.Month;

            return _CM(from, se);
        }
        public string MA3(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.SelectedDateTime = se.SelectedDateTime.AddMonths(3);
            se.Year = se.SelectedDateTime.Year;
            se.Month = se.SelectedDateTime.Month;

            return _CM(from, se);
        }

        public string N_1_CM(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Year -= 1;
            return _CM(from, se);
        }

        public string N_2_CM(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Year -= 2;
            return _CM(from, se);
        }

        public string N_1_Dec_CM(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Year -= 1;
            se.Month = 12;

            return _CM(from, se);
        }

        public string _CM(ARTReportFromItem from, SearchEntity se)
        {
            se.Month = from.month != 0 ? from.month : se.Month;
            Preprocess(from, se);
            if (from.merge.IsNotNullOrEmpty())
                return GetMergeValueFromEntityItem(from, se);

            string value = GetFieldCache(from, se);
            if (value.IsNullOrEmpty())
            {
                var item = GetItem(from, se);
                value = item[GetFieldIdentity(from)];
                SetFieldCache(from, se, value);
            }
            return value;
        }


        //取当年至当月数据
        public string YTD(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            return _YTD(from, se);
        }

        public string Full_Y_YTD(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Month = 12;

            return _YTD(from, se);
        }

        public string M_1_YTD(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            if (se.Month == 1)
            {
                se.Year -= 1;
                se.Month = 12;
            }
            else
                se.Month -= 1;

            return _YTD(from, se);
        }

        public string N_1_YTD(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Year -= 1;
            return _YTD(from, se);
        }

        public string _YTD(ARTReportFromItem from, SearchEntity se)
        {
            se.Month = from.month != 0 ? from.month : se.Month;
            Preprocess(from, se);

            string value = GetFieldCache(from, se);
            if (value.IsNullOrEmpty())
            {
                var item = GetItem(from, se);
                value = item.GetYTDValue(from, se) + "";
                SetFieldCache(from, se, value);
            }
            return value;
        }

        public string MYTD(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Month = from.month != 0 ? from.month : se.Month;
            Preprocess(from, se);
            string value = GetFieldCache(from, se);
            if (value.IsNullOrEmpty())
            {
                var item = GetItem(from, se);

                value = item.GetMYTDValue(from, se) + "";

                SetFieldCache(from, se, value);
            }
            return value;

        }

        public string SYTD(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;

            bool s1 = se.Month <= 3;
            bool s2 = !s1 && se.Month <= 6;
            bool s3 = !s1 && !s2 && se.Month <= 9;
            bool s4 = !s1 && !s2 && !s3 && se.Month <= 12;
            se.Month = s1 ? 3 : se.Month;
            se.Month = s2 ? 6 : se.Month;
            se.Month = s3 ? 9 : se.Month;
            se.Month = s4 ? 12 : se.Month;

            Preprocess(from, se);

            string value = GetFieldCache(from, se);
            if (value.IsNullOrEmpty())
            {
                var item = GetItem(from, se);
                if (item == null) return "0";
                value = item.GetYTDValue(from, se) + "";
                SetFieldCache(from, se, value);
            }
            return value;
        }

        //取历史12个月的数据
        public string R12(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            return _R12(from, se);
        }

        public string N_1_R12(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Year -= 1;
            return _R12(from, se);
        }

        public string _R12(ARTReportFromItem from, SearchEntity se)
        {
            se.Month = from.month != 0 ? from.month : se.Month;

            Preprocess(from, se);
            string value = GetFieldCache(from, se);
            if (value.IsNullOrEmpty())
            {
                var item = GetItem(from, se);
                if (item == null) return "0";
                value = item.GetL12Value(from, se) + "";
                SetFieldCache(from, se, value);
            }
            return value;
        }

        //取历史12个月的数据平均值
        public string L12Avg(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            return _L12Avg(from, se);
        }

        public string N_1_L12Avg(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Year -= 1;
            return _L12Avg(from, se);
        }

        public string _L12Avg(ARTReportFromItem from, SearchEntity se)
        {
            se.Month = from.month != 0 ? from.month : se.Month;
            Preprocess(from, se);
            string value = GetFieldCache(from, se);
            if (value.IsNullOrEmpty())
            {
                var item = GetItem(from, se);
                value = item.GetL12Value(from, se) / 12 + "";
                SetFieldCache(from, se, value);
            }
            return value;
        }

        List<BaseMergeRow> N1ActualYTDMergeRows;
        //Forecast standard report YTD 新的计算方式
        public string N1FCSTYTD(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            se.Year -= 1;
            se.Month += 3;
            if (N1ActualYTDMergeRows == null)
            {
                N1ActualYTDMergeRows = GetMergeRows(from, se);
            }

            var mergeRow = N1ActualYTDMergeRows.FirstOrDefault(p => p.accountcode == from.identity);
            return mergeRow.TryGetValue(from.field) + "";

        }

        private static List<BaseMergeRow> GetMergeRows(ARTReportFromItem from, SearchEntity se)
        {
            var sourceJSPath = HttpContext.Current.Server.MapPath("/_layouts/CReport/Forecast/YTDJS/" + from.list + ".js");
            var sourceJS = string.Empty;
            using (StreamReader sr = new StreamReader(sourceJSPath))
            {
                sourceJS = sr.ReadToEnd();
            }
            var fs = new FSSearchEntity()
            {
                Year = (se.Year + ""),
                Month = (se.Month + ""),
                Data = sourceJS,
                CompanyCode = "Asia",
                ListTitle = from.list
            };

            var res = new MergeServices().Excute(HttpContext.Current, fs);
            List<BaseMergeRow> mergeRows = res.Data as List<BaseMergeRow>;  //得到+3月的数据列表
            return mergeRows;
        }

        private static List<BaseMergeRow> GetFCSTMergeRows(ARTReportFromItem from, SearchEntity se, string list,string replaceStr,string afterStr)
        {
            var sourceJSPath = HttpContext.Current.Server.MapPath("/_layouts/CReport/Forecast/YTDJS/" + list + ".js");
            var sourceJS = string.Empty;
            using (StreamReader sr = new StreamReader(sourceJSPath))
            {
                sourceJS = sr.ReadToEnd();
            }
            if (replaceStr.IsNotNullOrEmpty())
                sourceJS = sourceJS.Replace(replaceStr, afterStr);

            var fs = new FSSearchEntity()
            {
                Year = (se.Year + ""),
                Month = (se.Month + ""),
                Data = sourceJS,
                CompanyCode = "Asia",
                ListTitle = list
            };

            var res = new MergeServices().Excute(HttpContext.Current, fs);
            List<BaseMergeRow> mergeRows = res.Data as List<BaseMergeRow>;  //得到+3月的数据列表
            return mergeRows;
        }

        List<BaseMergeRow> ActualYTDMergeRows;
        List<BaseMergeRow> FCSTYTDMergeRows;
        public string FCSTYTD(ARTReportFromItem from)
        {
            var se = ReportContext.Clone() as SearchEntity;
            if (ActualYTDMergeRows == null || FCSTYTDMergeRows == null)
            {
                ActualYTDMergeRows = GetFCSTMergeRows(from, se, from.list, "ForceRate: \"\",", "ForceRate: \"YTDM_3\",ForceRateType : \"Forecast\",");
                FCSTYTDMergeRows = GetFCSTMergeRows(from, se, "R_Forecast_" + from.list.Split('_').Last(),"","");
            }

            var ActualProIdentity = from.identity.Substring(0, 2);
            var FCSTIdentity = from.identity.Replace(ActualProIdentity + "02", ActualProIdentity + "04");


            var actualMergeRow = ActualYTDMergeRows.FirstOrDefault(p => p.accountcode == from.identity) as ActualMergeRow;
            var FCSTMergeRow = FCSTYTDMergeRows.FirstOrDefault(p => p.accountcode == FCSTIdentity) as ForecastMergeRow;

            var actualValue = actualMergeRow.YTD;

            actualValue += FCSTMergeRow.M_1;
            actualValue += FCSTMergeRow.M_2;
            actualValue += FCSTMergeRow.M_3;

            return actualValue + "";
        }
        #endregion


    }

}
